package com.zzb.framework.query;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.zzb.framework.common.BasePage;
import com.zzb.framework.common.PageSearchForm;
import com.zzb.framework.query.sqlbuilder.CountSqlBuilder;
import com.zzb.framework.query.sqlbuilder.OrderByBuilder;
import com.zzb.framework.query.sqlbuilder.PagionageSqlBuilder;
import com.zzb.framework.query.sqlbuilder.QueryMethodBuilder;
import com.zzb.framework.utils.ReflectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * genericQueryDAO 接口 支持Mysql Postgres 切换
 * Created by zhangzhenbin on 17-8-9.
 */
public interface GenericQueryDAO {
    Logger getLogger();
    PagionageSqlBuilder getPagionageSqlBuilder();
    JdbcTemplate getJdbcTemplate();

    default  <T> BasePage<T> queryPage(String querySql, Class<T> clazz, int page, int pageSize, Object... params) throws QueryException {
        try {
            String countSql = CountSqlBuilder.getCountSql(querySql);
            String limitSql = getPagionageSqlBuilder().build(page, pageSize);
            querySql = querySql + limitSql;
            long total = getJdbcTemplate().queryForObject(countSql, Long.class, params);
            List<T> list = getJdbcTemplate().query(querySql, new AnnotationColumnRowMapper<T>(clazz), params);
            return new BasePage<T>(list, page, pageSize, total);
        } catch (DataAccessException e) {
            throw new QueryException("DataAccessException", e);
        }
    }

    default  <S extends IPageSearchForm, T> BasePage<T> queryPage(S searchForm, Class<T> clazz, String ... tableNames) {
        try {
            //构建预定义系统
            List<QueryMethodItem> methodItemList = QueryMethodBuilder.build(searchForm, clazz);
            String tableName;
            if(ArrayUtils.isEmpty(tableNames)){
                TableName table = clazz.getAnnotation(TableName.class);
                if(table  != null){
                    tableName  = table.value();
                } else {
                    tableName = clazz.getName();
                }
            } else {
                tableName = tableNames[0];
            }
            Field[] allDeclaredFields = ReflectionUtils.getAllDeclaredFields(clazz);

            //查询SQL初始化
            String querySql = "select t.* ";
            String countSql = " select count(1) ";

            //查询条件
            StringBuilder sqlCondition =
                    new StringBuilder(" from " + tableName + " t where 1= 1 ");
            List<Object> params = new ArrayList<>();
            QueryMethodBuilder.methodItemListBuilder(methodItemList, sqlCondition, params);

            Map<String ,String> propertyColumnNameMap = new HashMap<>();

            //针对@TableLogic注解进行处理
            for (Field field : allDeclaredFields) {
                TableLogic tableLogic = field.getAnnotation(TableLogic.class);
                TableField tableField = field.getAnnotation(TableField.class);
                TableId tableId = field.getAnnotation(TableId.class);
                if(tableLogic != null){
                    String activateValue = tableLogic.value();
                    String columnName = null;
                    if(tableField != null) {
                        columnName = tableField.value();
                    } else {
                        columnName = field.getName();
                    }
                    sqlCondition.append(" and t." +columnName + " = " + activateValue);
                }
                if(tableId != null){
                    propertyColumnNameMap.put(field.getName(),tableId.value());
                }

                if(tableField != null){
                    propertyColumnNameMap.put(field.getName(),tableField.value());
                }

            }

            //追加AdditionalCondition
            if(StringUtils.isNotBlank(searchForm.getAdditionalCondition())) {
                sqlCondition.append(searchForm.getAdditionalCondition());
            }

            //排序
            StringBuilder orderBy = new StringBuilder();
            if (StringUtils.isNotBlank(searchForm.getOrderBy())) {

                if (!searchForm.getOrderBy().contains("order by")) {
                    String[] orderByGroups = searchForm.getOrderBy().split(",");
                    for (String orderByGroup : orderByGroups) {
                        String[] orderAndSort = orderByGroup.split(" ");
                        String orderColumn= "";
                        String orderSort = "asc";
                        for (String s : orderAndSort) {
                            if(StringUtils.isNotBlank(s)){
                                if(s.equalsIgnoreCase("asc") || s.equalsIgnoreCase("desc")){
                                    orderSort = s;
                                } else {
                                    orderColumn = propertyColumnNameMap.getOrDefault(s,s);
                                }
                            }
                        }
                        if(orderColumn.length() > 0) {
                            if (orderBy.length() != 0) {
                                orderBy.append(",");
                            } else {
                                orderBy.append(" order by ");
                            }
                            orderBy.append(orderColumn).append(" ").append(orderSort);
                        }
                    }
                }
            } else {
                orderBy = new StringBuilder(OrderByBuilder.getOrderByFields(allDeclaredFields));
            }

            //分页
            String limitString = getPagionageSqlBuilder().build(searchForm.getPage(),searchForm.getPageSize());

            querySql = querySql + sqlCondition.toString() + orderBy.toString() + limitString;
            countSql += sqlCondition;
            List<T> list = getJdbcTemplate().query(querySql, new AnnotationColumnRowMapper<T>(clazz), params.toArray());
            Long count = getJdbcTemplate().queryForObject(countSql, Long.class, params.toArray());
            getLogger().info(querySql);

            return new BasePage<>(list, searchForm.getPage(), searchForm.getPageSize(), count);
        } catch (IllegalAccessException  e) {
            e.printStackTrace();
            return null;
        }
    }
}
